home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / Think Class Libraries / CPrefsFile & Friends 1.0 / CPrefsFile.c < prev    next >
Encoding:
Text File  |  1994-11-30  |  26.7 KB  |  986 lines  |  [TEXT/KAHL]

  1. /*
  2.  * CPrefsFile.c
  3.  * This THINK C 5 object, which must be subclassed, makes storing data in
  4.  * the Preferences folder almost painless.  It requires the TCL.
  5.  * Version 1.1, 13 April 1992
  6.  *
  7.  * by Jamie McCarthy
  8.  * © Copyright 1992 by James R. McCarthy.  All rights reserved.
  9.  * This code can be both distributed and used freely.
  10.  * Internet: k044477@kzoo.edu            AppleLink: j.mccarthy
  11.  * Telephone:  800-421-4157 or US 616-665-7075 (9:00-5:00 Eastern time)
  12.  * I'm releasing this code with the hope that someone will get something
  13.  * out of it.  Feedback of any sort, even just letting me know that you're
  14.  * using it, is greatly appreciated!
  15.  *
  16.  * To implement preferences, you need to do two things:  create a subclass
  17.  * of CPrefsFile tailored to your application, and add a preferences
  18.  * resource to your project resource file.  It is strongly recommended
  19.  * that you also do a third thing, namely create a 'STR#' resource for
  20.  * CPrefsFile to use.
  21.  *
  22.  * There are two sample subclasses distributed with CPrefsFile, COOPrefs
  23.  * and CGlobalPrefs.  There is a sample preferences resource and 'STR#'
  24.  * resource that could be used with either of them.
  25.  *
  26.  * BRIEF OVERVIEW OF CPREFSFILE:
  27.  *
  28.  * CPrefsFile supports preferences stored in one (1) resource.  CPrefsFile
  29.  * takes care of the mundane stuff:  finding the Preferences folder and
  30.  * doing the opening, closing, reading, and writing from the file.  Your
  31.  * subclass has to help by overriding three methods, and you may want to
  32.  * write access methods to get at the resource.
  33.  *
  34.  * Everything is extremely customizable.  I spent quite some time making
  35.  * the class as general as possible, and as override-able as possible.
  36.  *
  37.  * IPrefsFile() takes three arguments:  its strings ID, the preferences
  38.  * file's fileType, and a Boolean indicating whether or not to look for
  39.  * the preferences file in the same folder as the application.  The
  40.  * strings ID is discussed later.  The fileType should be whatever you
  41.  * like.  ('PREF' is suggested;  it seems to be a de facto standard.)
  42.  *
  43.  * When the object is initialized, the prefs are read in.  Your prefs
  44.  * are thus always accessible, from the time you call
  45.  * IYourSubclassOfPrefsFile() to the time you Dispose() of it.  The
  46.  * preferences file is always kept open.
  47.  *
  48.  * To write the current (possibly modified) preferences to disk, call
  49.  * writePrefs().  To throw away the current prefs and restore them to
  50.  * what's in the preferences file, call readPrefs().  To throw away the
  51.  * current prefs and restore the defaults, call readDefaultPrefs().  To
  52.  * throw away the prefs on disk and replace them with the defaults,
  53.  * call readDefaultPrefs() followed by writePrefs().
  54.  *
  55.  * THE BIG THREE METHOD OVERRIDES:
  56.  *
  57.  * You must override assignToPrefsHndl(), valueOfPrefsHndl(), and
  58.  * sizeOfPrefs().  The sample code succinctly demonstrates how to
  59.  * do this.
  60.  *
  61.  * THE 'STR#' RESOURCE:
  62.  *
  63.  * This resource is optional, but recommended;  if it's not there,
  64.  * CPrefsFile uses the default, non-localizable, English strings when
  65.  * errors occur.  Pass the resource's ID in strsID to IPrefsFile, or
  66.  * zero for no resource.
  67.  *
  68.  * If any string is either blank (length zero) or missing, the default
  69.  * string will be used in its place.
  70.  *
  71.  * The first string in the list is the text that will be displayed if
  72.  * the prefs in the preferences file are invalid.  The default is
  73.  * #defined as "kPFPrefsWereInvalidDefaultStr".
  74.  *
  75.  * The second string is the text that will be displayed if the default
  76.  * prefs, stored in the application or the project resource file, are
  77.  * invalid.  The default is "kPFDefaultPrefsWereInvalidDefaultStr".
  78.  *
  79.  * The third string is the beginning of the notice that will be
  80.  * displayed if the preferences file cannot be written.  The default
  81.  * is "kPFCannotWriteDefaultStr".
  82.  *
  83.  * The fourth string is the end of the notice, if the file is locked.
  84.  * The default is "kPFFileLockedDefaultStr".
  85.  *
  86.  * The sixth string is the end of the notice, if the disk is locked.
  87.  * The default is "kPFDiskLockedDefaultStr".
  88.  *
  89.  * The fifth string is the end of the notice, if the file is busy.
  90.  * The default is "kPFFileBusyDefaultStr".
  91.  *
  92.  * The seventh string is the end of the notice, if there is something
  93.  * else that's preventing the file from being written. The default
  94.  * is "kPFUnknownErrorDefaultStr".
  95.  *
  96.  * The eighth string is the name of your application.  Use this string
  97.  * only if you want copies of your app with differing names to use the
  98.  * same preferences file.  For example, you might want both "MyApp 1.0"
  99.  * and "MyApp 1.1" to use the file "MyApp Prefs".  The default is the
  100.  * current name of the app.
  101.  *
  102.  * The ninth string is the name of the preferences folder, if Gestalt
  103.  * can't find it.  The default is the English "Preferences".  I'm not
  104.  * sure why you'd want to change this, unless your application was
  105.  * going to be used exclusively on (say) French systems.  Then, you'd
  106.  * put in "Les Préferènçieresment" or whatever the French folder is named
  107.  * under System 7, so that if your user ran the program under both System
  108.  * 6 and 7, her preferences would always be in the right place.  Most
  109.  * people should just leave this blank.
  110.  *
  111.  * The tenth string is reserved for future use and should be left blank.
  112.  *
  113.  * The eleventh and subsequent strings are suffixes, one of which will be
  114.  * appended to the name of your app to produce the name of the prefs file.
  115.  * If none are found, " Preferences" or " Prefs" (note the leading spaces)
  116.  * is used.  It uses the first suffix that, together with the application
  117.  * name, comes to under 31 characters.  If all the suffices are too long,
  118.  * it uses the last (or " Prefs"), and trims the app name down with an
  119.  * ellipsis to make the name exactly 30 characters long.
  120.  *
  121.  * If you have every string--that is, if every entry of the 'STR#' is
  122.  * meaningfully filled out--you can #define the constact
  123.  * "everyCPrefsFileStrIsGuaranteed" in your project prefix to be TRUE.
  124.  * That'll save you a few bytes of string data.
  125.  *
  126.  * EASY CUSTOMIZATION OF CPREFSFILE:
  127.  *
  128.  * If you want your preferences resource to be stored with a different
  129.  * type or id, you may override the getRsrc...() methods as well.  (For
  130.  * example, you might want to do this so your ResEdit template doesn't
  131.  * conflict with someone else's.)  The resource name doesn't matter and
  132.  * is normally blank;  override getRsrcName() if you don't like that.
  133.  *
  134.  * Prefs are tested for "acceptability" after they are read from and
  135.  * before they are written to disk.  If they fail the test, the
  136.  * default prefs are used instead.  The default test is just to check
  137.  * the length of the handle.  If you want more strenuous tests,
  138.  * override areTheCurrentPrefsAcceptable() and do them yourself!
  139.  * You'll probably want to "&&" CPrefsFile's result with yours.
  140.  *
  141.  * If you never want to create a file on disk, override
  142.  * getPrefsCanBeWritten() to return FALSE.
  143.  *
  144.  * LESS EASY CUSTOMIZATION OF CPREFSFILE:
  145.  *
  146.  * The remaining methods of CPrefsFile are divided into four groups:
  147.  * low-level convenience methods, preference-manipulation methods,
  148.  * acceptability-checking methods, and the three high-level methods.
  149.  * The first and last groups you'll probably never need to touch;
  150.  * if you do, you won't need my help anyway.  In the third group,
  151.  * you may want to fiddle with displayAlert() if you want a fancier
  152.  * way of indicating invalid preferences, but that should be fairly
  153.  * straightforward.
  154.  *
  155.  * CPrefsFile raises an exception for invalid default prefs.  You
  156.  * should only get that error if you've forgotten to change them,
  157.  * or if your application has been tampered with.  Nonetheless, if
  158.  * you prefer to handle the exception yourself, put something
  159.  * appropriate into the CATCH handler for the writePrefs() or
  160.  * readDefaultPrefs() call.  Again, you probably won't need my help.
  161.  *
  162.  * If you want to store the preferences differently, however, you'll
  163.  * want to override the three methods in the second group:
  164.  * forgetPrefsHndl(), getPrefsHndl(), and addPrefsHndl().  By
  165.  * default, these three just call DisposHandle(), Get1Resource(),
  166.  * and AddResource(), respectively, on your prefs handle.  If, for
  167.  * example, you want your preferences spread out over ten resources,
  168.  * you'll have to override these to handle those extra resources.
  169.  * CPrefsFile does some miscellaneous calls on your "main" prefs
  170.  * resource (the one that getPrefsHndl() returns), such as
  171.  * DetachResource(), but it bottlenecks through these three methods
  172.  * to dispose, read, or write.
  173.  *
  174.  * CHANGE HISTORY:
  175.  *
  176.  * Changes from 1.0 to 1.1:
  177.  * Added seven error strings to the 'STR#' list.
  178.  * Put default strings and string indices into constants.
  179.  * Bottlenecked default string handling through getIndPrefsString().
  180.  *    (Suggested by R.Exner@trl.oz.au.)
  181.  * Put in tests for the file being unwriteable and unreadable.
  182.  * Added sizeOfPrefs() and areTheCurrentPrefsAcceptable() calls.
  183.  * Removed getFilePermission() and added getPrefsCanBeWritten().
  184.  * Added ForgetResource() of the 'STR#' after using it.
  185.  * Added the constant everyCPrefsFileStrIsGuaranteed.
  186.  * Did miscellaneous code clean-ups.
  187.  *
  188.  * FUTURE PLANS:
  189.  *
  190.  * Near future:  a subclass that easily handles remembering
  191.  *    application windows' (not document windows') placement
  192.  *    on-screen.  (Suggested by R.Exner@trl.oz.au.)
  193.  * Far future:  a subclass that reads a specified 'TMPL' from the
  194.  *    app and (if necessary) modifies the prefs resource and your
  195.  *    subclass' _code_, making adding prefs a snap.
  196.  * Very far future:  a subclass that does the same thing from a
  197.  *    'DITL', and also handles display and editing.  Wouldn't
  198.  *    codeless preferences be wonderful?
  199.  *
  200.  */
  201.  
  202.  
  203.  
  204. /******************************/
  205.  
  206. #include "CPrefsFile.h"
  207.  
  208. /******************************/
  209.  
  210. #include <Errors.h>
  211. #include <GestaltEqu.h>
  212. #include <Folders.h>
  213.  
  214. #include <Constants.h>
  215. #include <TBUtilities.h>
  216. #include <CError.h>
  217.  
  218. /******************************/
  219.  
  220. enum {
  221.     kPFPrefsWereInvalidStrIndex = 1,
  222.     kPFDefaultPrefsWereInvalidStrIndex,
  223.     kPFCannotWriteStrIndex,
  224.     kPFFileLockedStrIndex,
  225.     kPFDiskLockedStrIndex,
  226.     kPFFileBusyStrIndex,
  227.     kPFUnknownErrorStrIndex,
  228.     kPFApplicationNameStrIndex,
  229.     kPFPrefsFolderNameStrIndex,
  230.     kPFReserved,
  231.     kPFFirstPrefsSuffixStrIndex
  232. } ;
  233.  
  234. #define kPFEmptyStr ((unsigned char *) "\p")
  235. #define kPFPrefsWereInvalidDefaultStr ((unsigned char *) \
  236.     "\pThe preferences were invalid, and were replaced by the defaults.")
  237. #define kPFDefaultPrefsWereInvalidDefaultStr ((unsigned char *) \
  238.     "\pThe default preferences are invalid. This is a fatal error.")
  239. #define kPFCannotWriteDefaultStr ((unsigned char *) \
  240.     "\pYou won’t be able to save preferences, because ")
  241. #define kPFFileLockedDefaultStr ((unsigned char *) \
  242.     "\pthe file is locked.")
  243. #define kPFDiskLockedDefaultStr ((unsigned char *) \
  244.     "\pthe disk is locked.")
  245. #define kPFFileBusyDefaultStr ((unsigned char *) \
  246.     "\pthe file is busy.")
  247. #define kPFUnknownErrorDefaultStr ((unsigned char *) \
  248.     "\pof an unexpected error.")
  249. #define kPFPrefsFolderNameDefaultStr ((unsigned char *) "\pPreferences")
  250. #define kPFLongPrefsSuffixDefaultStr ((unsigned char *) "\p Preferences")
  251. #define kPFShortPrefsSuffixDefaultStr ((unsigned char *) "\p Prefs")
  252.  
  253. #if !defined(everyCPrefsFileStrIsGuaranteed)
  254. #define everyCPrefsFileStrIsGuaranteed (FALSE)
  255. #endif
  256.  
  257. /******************************/
  258.  
  259.  
  260.  
  261. void CPrefsFile::IPrefsFile(short strsID, OSType fileType,
  262.     Boolean lookInApplFolderAlso)
  263. {
  264.         /*
  265.          * This initialization procedure must be called before opening
  266.          * any resource files besides the application, or it will not
  267.          * work when run as a project.  Running as a standalone should
  268.          * work in any case, but why tempt fate?
  269.          */
  270.     
  271.     Handle dummyHndl;
  272.     Boolean wasLocked;
  273.     extern OSType gSignature;
  274.     
  275.     inherited::IResFile();
  276.     
  277.     wasLocked = Lock(TRUE);
  278.     
  279.     itsStrsID = strsID;
  280.     itsFileType = fileType;
  281.     itsRsrcType = getRsrcType();
  282.     itsRsrcID = getRsrcID();
  283.     getRsrcName(itsRsrcName);
  284.     itsPermissionType = fsRdWrPerm;
  285.     itsPrefsCouldBeWritten = TRUE;
  286.     isReadingDefaultPrefs = FALSE;
  287.     
  288.     GetAppParms(itsApplName, &itsApplRefNum, &dummyHndl);
  289.     itsProjRsrcRefNum = CurResFile();
  290.     if (itsProjRsrcRefNum == itsApplRefNum) itsProjRsrcRefNum = 0;
  291.     
  292.     Lock(wasLocked);
  293.     
  294.     findName();
  295.     findFolder(lookInApplFolderAlso);
  296.     
  297.     forgetStrListResource();
  298.     
  299.     if (!ExistsOnDisk()) {
  300.         tryToCreateNew();
  301.     }
  302.     
  303.     if (ExistsOnDisk()) {
  304.         tryToOpen();
  305.     }
  306.     
  307.         /*
  308.          * This will read from the file if it's open, and from the app
  309.          * if it's not.
  310.          */
  311.     readPrefs();
  312.     
  313.     if (getPrefsCanBeWritten()) {
  314.         tryToWrite();
  315.     }
  316. }
  317.  
  318.  
  319.  
  320. void CPrefsFile::Dispose(void)
  321.     /* Subclasses shouldn't need to override this method unless they allocate
  322.      * memory in their initialization method.  If you need to clean up memory
  323.      * which you allocated in readPrefs or readDefaultPrefs (which you won't,
  324.      * unless you override them), do so in forgetPrefsHndl.
  325.      */
  326. {
  327.     forgetPrefsHndl();
  328.     inherited::Dispose();
  329. }
  330.  
  331.  
  332.  
  333. Boolean CPrefsFile::getPrefsCanBeWritten(void)
  334. {
  335.     return ExistsOnDisk() && itsPrefsCouldBeWritten;
  336. }
  337.  
  338.  
  339.  
  340. OSType CPrefsFile::getRsrcType(void)
  341.     /* Return the resource type that the preferences are stored in.
  342.      * Subclasses may override this method, but they don't have to.
  343.      */
  344. {
  345.     return 'PREF';
  346. }
  347.  
  348.  
  349.  
  350. short CPrefsFile::getRsrcID(void)
  351.     /* Return the resource ID that the preferences are stored in.
  352.      * Subclasses may override this method, but they don't have to.
  353.      */
  354. {
  355.     return 128;
  356. }
  357.  
  358.  
  359.  
  360. void CPrefsFile::getRsrcName(Str63 theName)
  361.     /* Return the name of the resource that the preferences are stored in.
  362.      * Note that this is used for writing only; the name isn't
  363.      * significant when the prefs are read.
  364.      * Subclasses may override this method, but they don't have to.
  365.      */
  366. {
  367.     FailNIL(theName);
  368.     CopyPString(kPFEmptyStr, theName);
  369. }
  370.  
  371.  
  372.  
  373. Boolean CPrefsFile::areTheCurrentPrefsAcceptable(void)
  374.     /* This is called by checkPrefsForAcceptability().  It returns TRUE
  375.      * if the prefs handle contains reasonable values.  You can redefine
  376.      * "reasonable" by overriding this method, though you don't have to.
  377.      * This code defines "reasonable" as "being of the right length."
  378.      */
  379. {
  380.     Boolean returnValue = FALSE;
  381.     FailNIL(valueOfPrefsHndl());
  382.     returnValue = (GetHandleSize(valueOfPrefsHndl()) == sizeOfPrefs());
  383.     return returnValue;
  384. }
  385.  
  386.  
  387.  
  388. void CPrefsFile::assignToPrefsHndl(Handle theHndl)
  389.     /* Your subclass must override this method.  It's pretty simple--
  390.      * you just assign the generic-type Handle to your own handle.  This
  391.      * method serves only to handle the type coercion gracefully.
  392.      */
  393. {
  394.     SubclassResponsibility();
  395. }
  396.  
  397.  
  398.  
  399. Handle CPrefsFile::valueOfPrefsHndl(void)
  400.     /* Your subclass must override this method.  It's pretty simple--
  401.      * you just return your own handle as a (Handle).  This method serves
  402.      * only to handle the type coercion gracefully.
  403.      */
  404. {
  405.     SubclassResponsibility();
  406. }
  407.  
  408.  
  409.  
  410. short CPrefsFile::sizeOfPrefs(void)
  411.     /* Your subclass must override this method.  It's a one-liner--
  412.      * you just return the size of your preferences data.
  413.      */
  414. {
  415.     SubclassResponsibility();
  416. }
  417.  
  418.  
  419.  
  420. Boolean CPrefsFile::getIndPrefsString(unsigned char *theString, short index)
  421.     /* The return value indicates whether the string was found in a
  422.      * resource (TRUE), or was "hard-coded" in (FALSE);  that value
  423.      * can almost always be ignored.
  424.      * Subclasses may override this method, but normally shouldn't have to.
  425.      */
  426. {
  427.     if (itsStrsID != 0) {
  428.         GetIndString(theString, itsStrsID, index);
  429.     } else {
  430.         CopyPString(kPFEmptyStr, theString);
  431.     }
  432.     
  433.     if (theString[0] != 0) {
  434.         
  435.             /* The string was in the resource file. */
  436.         
  437.         return TRUE;
  438.         
  439.     }
  440.     
  441. #if (!everyCPrefsFileStrIsGuaranteed)
  442.     
  443.     else {
  444.         
  445.             /* The string was blank or not there.  Substitute the default. */
  446.         
  447.         switch (index) {
  448.             
  449.             case kPFPrefsWereInvalidStrIndex:
  450.                 CopyPString(kPFPrefsWereInvalidDefaultStr, theString);
  451.                 break;
  452.                 
  453.             case kPFDefaultPrefsWereInvalidStrIndex:
  454.                 CopyPString(kPFDefaultPrefsWereInvalidDefaultStr, theString);
  455.                 break;
  456.                 
  457.             case kPFCannotWriteStrIndex:
  458.                 CopyPString(kPFCannotWriteDefaultStr, theString);
  459.                 break;
  460.                 
  461.             case kPFFileBusyStrIndex:
  462.                 CopyPString(kPFFileBusyDefaultStr, theString);
  463.                 break;
  464.                 
  465.             case kPFDiskLockedStrIndex:
  466.                 CopyPString(kPFDiskLockedDefaultStr, theString);
  467.                 break;
  468.                 
  469.             case kPFUnknownErrorStrIndex:
  470.                 CopyPString(kPFUnknownErrorDefaultStr, theString);
  471.                 break;
  472.                 
  473.             case kPFApplicationNameStrIndex:
  474.                 CopyPString(this->itsApplName, theString);
  475.                 break;
  476.                 
  477.             case kPFPrefsFolderNameStrIndex:
  478.                 CopyPString(kPFPrefsFolderNameDefaultStr, theString);
  479.                 break;
  480.                 
  481.             case kPFFirstPrefsSuffixStrIndex:
  482.                 CopyPString(kPFLongPrefsSuffixDefaultStr, theString);
  483.                 break;
  484.                 
  485.             case kPFFirstPrefsSuffixStrIndex + 1:
  486.                 CopyPString(kPFShortPrefsSuffixDefaultStr, theString);
  487.                 break;
  488.                 
  489.             case kPFReserved:
  490.             default:
  491.                     /* Leave the string blank. */
  492.                 break;
  493.                 
  494.         }
  495.     }
  496.     
  497. #endif
  498.     
  499.     return FALSE;
  500. }
  501.  
  502.  
  503.  
  504. void CPrefsFile::displayAlert(short firstIndex, short secondIndex)
  505.     /* The alert-displaying code was stolen from CError::PostAlert().
  506.      * Subclasses are welcome to override this method,
  507.      * but they don't have to.
  508.      */
  509. {
  510.     Str255 firstStr, secondStr;
  511.     
  512.     getIndPrefsString(firstStr, firstIndex);
  513.     if (secondIndex >= 1) {
  514.         getIndPrefsString(secondStr, secondIndex);
  515.     } else {
  516.         secondStr[0] = 0;
  517.     }
  518.     
  519.     ConcatPStrings(firstStr, secondStr);
  520.     
  521.     ParamText(firstStr, NULL, NULL, NULL);
  522.     PositionDialog('ALRT', ALRTgeneral);
  523.     InitCursor();
  524.     Alert(ALRTgeneral, NULL);
  525.     
  526.     forgetStrListResource();
  527. }
  528.  
  529.  
  530.  
  531. void CPrefsFile::forgetStrListResource(void)
  532.     /* Subclasses may override this method, but they don't have to. */
  533. {
  534.     Handle theStrsResource;
  535.     theStrsResource = GetResource('STR#', itsStrsID);
  536.     ForgetResource(theStrsResource);
  537. }
  538.  
  539.  
  540.  
  541. void CPrefsFile::findName(void)
  542.     /* Subclasses should not use or override this method. */
  543. {
  544.     Str63 theName, suffix;
  545.     short cStrIndex;
  546.     Boolean foundName;
  547.     Boolean stillHaveSuffices;
  548.     
  549.     foundName = FALSE;
  550.     stillHaveSuffices = TRUE;
  551.     
  552.     getIndPrefsString(theName, kPFApplicationNameStrIndex);
  553.     
  554.     cStrIndex = kPFFirstPrefsSuffixStrIndex;
  555.     
  556.     while (!foundName && stillHaveSuffices) {
  557.         getIndPrefsString(suffix, cStrIndex);
  558.         if (suffix[0] == 0) {
  559.             stillHaveSuffices = FALSE;
  560.         } else {
  561.             if (theName[0] + suffix[0] < 31) {
  562.                 ConcatPStrings(theName, suffix);
  563.                 foundName = TRUE;
  564.             } else {
  565.                 ++cStrIndex;
  566.             }
  567.         }
  568.     }
  569.     
  570.     if (!foundName) {
  571.             /* None of the strings were short enough.  Use the last one,
  572.              * and squish the application's name to make it fit.
  573.              */
  574.         getIndPrefsString(suffix, cStrIndex - 1);
  575.         theName[0] = 30-suffix[0];
  576.         theName[theName[0]] = '…';
  577.         ConcatPStrings(theName, suffix);
  578.     }
  579.     
  580.     CopyPString(theName, this->name);
  581. }
  582.  
  583.  
  584.  
  585. void CPrefsFile::findFolder(Boolean lookInApplFolderAlso)
  586.     /* Subclasses should not use or override this method. */
  587. {
  588.     Boolean foundFolder;
  589.     short myFoundVRefNum;
  590.     long myFoundDirID;
  591.     HParamBlockRec params;
  592.     CInfoPBRec cInfo;
  593.     extern tSystem gSystem;
  594.     
  595.     foundFolder = FALSE;
  596.     
  597.     if (lookInApplFolderAlso) {
  598.         
  599.             /* Look in the application's folder */
  600.         
  601.         Str63 volName;
  602.         WDPBRec wdpb;
  603.         
  604.             /* Get the current (application's folder) working directory */
  605.         FailOSErr(GetVol(volName, &myFoundVRefNum));
  606.         wdpb.ioCompletion = NULL;
  607.         wdpb.ioNamePtr = NULL;
  608.         wdpb.ioVRefNum = myFoundVRefNum;
  609.         wdpb.ioWDIndex = 0;
  610.         wdpb.ioWDProcID = 0;
  611.         wdpb.ioWDVRefNum = 0;
  612.         FailOSErr(PBGetWDInfo(&wdpb, FALSE));
  613.         
  614.         myFoundVRefNum = wdpb.ioWDVRefNum;
  615.         myFoundDirID = wdpb.ioWDDirID;
  616.         
  617.         this->volNum = myFoundVRefNum;
  618.         this->dirID = myFoundDirID;
  619.         
  620.         if (ExistsOnDisk()) {
  621.             foundFolder = TRUE;
  622.         }
  623.     }
  624.     
  625.     if (!foundFolder && gSystem.hasGestalt) {
  626.         
  627.             /* Use Gestalt to look in the Preferences folder */
  628.         
  629.         long foldAttr;
  630.         OSErr theOSErr;
  631.         
  632.         theOSErr = Gestalt(gestaltFindFolderAttr, &foldAttr);
  633.         if ((theOSErr == noErr) && (foldAttr & (1L << gestaltFindFolderPresent))) {
  634.             
  635.             FailOSErr(FindFolder(kOnSystemDisk, kPreferencesFolderType,
  636.                 TRUE, /* create the folder, if necessary */
  637.                 &myFoundVRefNum, &myFoundDirID));
  638.             foundFolder = TRUE;
  639.             
  640.         }
  641.         
  642.     }
  643.     
  644.     if (!foundFolder) {
  645.         
  646.             /* There's no Gestalt;  look manually in the Preferences folder */
  647.         
  648.         Str63 theFolder;
  649.         SysEnvRec theWorld;
  650.         OSErr theOSErr;
  651.         
  652.         theOSErr = SysEnvirons(2, &theWorld);
  653.         if (theOSErr == envNotPresent || theOSErr == envBadVers) {
  654.             FailOSErr(theOSErr);
  655.         }
  656.         
  657.         getIndPrefsString(theFolder, kPFPrefsFolderNameStrIndex);
  658.         
  659.         params.volumeParam.ioCompletion = NULL;
  660.         params.volumeParam.ioNamePtr = NULL;
  661.         params.volumeParam.ioVRefNum = theWorld.sysVRefNum;
  662.         params.volumeParam.ioVolIndex = 0;
  663.         FailOSErr(PBHGetVInfo(¶ms, FALSE));
  664.         
  665.         myFoundVRefNum = params.volumeParam.ioVRefNum;
  666.         
  667.             /* Look for the folder */
  668.         cInfo.dirInfo.ioCompletion = NULL;
  669.         cInfo.dirInfo.ioNamePtr = theFolder;
  670.         cInfo.dirInfo.ioVRefNum = theWorld.sysVRefNum;
  671.         cInfo.dirInfo.ioFDirIndex = 0;
  672.         cInfo.dirInfo.ioDrDirID = 0;
  673.         
  674.         if (PBGetCatInfo(&cInfo, FALSE) == fnfErr) {
  675.                 /* There's no folder in the blessed folder called "Preferences"
  676.                  * (or whatever we decided is its proper name).  Make one.
  677.                  */
  678.             FailOSErr(DirCreate(theWorld.sysVRefNum, 0, theFolder, &myFoundDirID));
  679. #if 0
  680.             params.fileParam.ioCompletion = NULL;
  681.             params.fileParam.ioNamePtr = theFolder;
  682.             params.fileParam.ioVRefNum = theWorld.sysVRefNum;
  683.             params.fileParam.ioFVersNum = 0;
  684.             params.fileParam.ioFDirIndex = 0;
  685.             params.fileParam.ioDirID = 0;
  686.             FailOSErr(PBDirCreate(¶ms, FALSE));
  687.             myFoundDirID = params.fileParam.ioDirID;
  688. #endif
  689.         } else {
  690.             myFoundDirID = cInfo.dirInfo.ioDrDirID;
  691.         }
  692.     }
  693.     
  694.     this->volNum = myFoundVRefNum;
  695.     this->dirID = myFoundDirID;
  696. }
  697.  
  698.  
  699.  
  700. void CPrefsFile::tryToCreateNew(void)
  701. {
  702.     TRY {
  703.         CreateNew(gSignature, itsFileType);
  704.     } CATCH {
  705.         itsPrefsCouldBeWritten = FALSE;
  706.         displayCannotWritePrefsAlert(gLastError);
  707.         NO_PROPAGATE;
  708.     } ENDTRY;
  709. }
  710.  
  711.  
  712.  
  713. void CPrefsFile::tryToOpen(void)
  714. {
  715.     TRY {
  716.         Open(itsPermissionType);
  717.     } CATCH {
  718.         itsPrefsCouldBeWritten = FALSE;
  719.         if (itsPermissionType != fsRdPerm) {
  720.             itsPermissionType = fsRdPerm;
  721.             displayCannotWritePrefsAlert(gLastError);
  722.             RETRY;
  723.         } else {
  724.             readDefaultPrefs();
  725.             NO_PROPAGATE;
  726.         }
  727.     } ENDTRY;
  728. }
  729.  
  730.  
  731.  
  732. void CPrefsFile::tryToWrite(void)
  733. {
  734.     short oldResFile;
  735.     Handle testHndl;
  736.     oldResFile = CurResFile();
  737.     testHndl = NULL;
  738.     TRY {
  739.         MakeCurrent();
  740.         testHndl = NewHandle(0);
  741.         AddResource(testHndl, 'test', 0, kPFEmptyStr);
  742.         FailResError();
  743.         RmveResource(testHndl);
  744.         ForgetHandle(testHndl);
  745.     } CATCH {
  746.         ReleaseResource(testHndl);
  747.         if (ResError() == resNotFound) {
  748.             ForgetHandle(testHndl);
  749.         } else {
  750.             testHndl = NULL;
  751.         }
  752.         UseResFile(oldResFile);
  753.         itsPrefsCouldBeWritten = FALSE;
  754.         displayCannotWritePrefsAlert(gLastError);
  755.         NO_PROPAGATE;
  756.     } ENDTRY;
  757. }
  758.  
  759.  
  760.  
  761. void CPrefsFile::forgetPrefsHndl(void)
  762. {
  763.     if (valueOfPrefsHndl() != NULL) {
  764.         DisposHandle(valueOfPrefsHndl());
  765.         assignToPrefsHndl(NULL);
  766.     }
  767. }
  768.  
  769.  
  770.  
  771. Handle CPrefsFile::getPrefsHndl(void)
  772. {
  773.     return Get1Resource(itsRsrcType, itsRsrcID);
  774. }
  775.  
  776.  
  777.  
  778. void CPrefsFile::addPrefsHndl(Handle theHndl)
  779. {
  780.     Str63 theRsrcName;
  781.     FailNIL(theHndl);
  782.     getRsrcName(theRsrcName);
  783.     AddResource(theHndl, itsRsrcType, itsRsrcID, theRsrcName);
  784. }
  785.  
  786.  
  787.  
  788. void CPrefsFile::displayCannotWritePrefsAlert(OSErr theReason)
  789. {
  790.     switch (theReason) {
  791.         
  792.         case afpAccessDenied:    // if locked when file sharing is on
  793.         case afpObjectLocked:    // if we're in a foreign file system (I think) (not bloody likely!)
  794.         case fLckdErr:
  795.         case permErr:                // could be anything, but this seems most likely
  796.             displayAlert(kPFCannotWriteStrIndex, kPFFileLockedStrIndex);
  797.             break;
  798.             
  799.         case wPrErr:
  800.         case vLckdErr:
  801.                 /* I don't distinguish between a hardware and a software lock.
  802.                  * Most people don't really care, do they?
  803.                  */
  804.             displayAlert(kPFCannotWriteStrIndex, kPFDiskLockedStrIndex);
  805.             break;
  806.             
  807.         case afpFileBusy:
  808.         case opWrErr:
  809.         case fBsyErr:
  810.                 /* Actually, I wouldn't expect to get either fBsyErr or permErr,
  811.                  * because we're dealing with the Resource Manager.  But you
  812.                  * never can tell with the File Mananger...
  813.                  */
  814.             displayAlert(kPFCannotWriteStrIndex, kPFFileBusyStrIndex);
  815.             break;
  816.             
  817.         default:
  818.             displayAlert(kPFCannotWriteStrIndex, kPFUnknownErrorStrIndex);
  819.             break;
  820.             
  821.     }
  822. }
  823.  
  824.  
  825.  
  826. void CPrefsFile::displayInvalidPrefsAlert(void)
  827. {
  828.     displayAlert(kPFPrefsWereInvalidStrIndex, 0);
  829. }
  830.  
  831.  
  832.  
  833. void CPrefsFile::displayInvalidDefaultPrefsAlert(void)
  834. {
  835.     displayAlert(kPFDefaultPrefsWereInvalidStrIndex, 0);
  836. }
  837.  
  838.  
  839.  
  840. void CPrefsFile::checkPrefsForAcceptability(void)
  841. {
  842.     if (!areTheCurrentPrefsAcceptable()) {
  843.         if (isReadingDefaultPrefs) {
  844.             displayInvalidDefaultPrefsAlert();
  845.             Failure(kSilentErr, 0);
  846.         } else {
  847.             displayInvalidPrefsAlert();
  848.             readDefaultPrefs();
  849.             if (getPrefsCanBeWritten()) writePrefs();
  850.         }
  851.     }
  852. }
  853.  
  854.  
  855.  
  856. void CPrefsFile::readPrefs(void)
  857.     /* Reads in preferences from the prefs file.  The resource is detached
  858.      * immediately after being read, so it's just a plain handle.
  859.      * Subclasses may override this method, but normally shouldn't have to.
  860.      */
  861. {
  862.     if (!IsOpen()) {
  863.         
  864.         readDefaultPrefs();
  865.         
  866.     } else {
  867.         
  868.         short oldResFile;
  869.         short theReturnVal;
  870.         
  871.         forgetPrefsHndl();
  872.         
  873.         oldResFile = CurResFile();
  874.         MakeCurrent();
  875.         
  876.         TRY {
  877.             assignToPrefsHndl(getPrefsHndl());
  878.             if (valueOfPrefsHndl() == NULL) {
  879.                 
  880.                     /* Silently put the default prefs into the file. */
  881.                 readDefaultPrefs();
  882.                 if (getPrefsCanBeWritten()) writePrefs();
  883.                 
  884.             } else {
  885.                 
  886.                 DetachResource(valueOfPrefsHndl());
  887.                 
  888.             }
  889.             
  890.             checkPrefsForAcceptability();
  891.             
  892.             UseResFile(oldResFile);
  893.         } CATCH {
  894.             UseResFile(oldResFile);
  895.         } ENDTRY;
  896.         
  897.     }
  898. }
  899.  
  900.  
  901.  
  902. void CPrefsFile::writePrefs(void)
  903.     /* Writes out preferences to the prefs file.  After this method
  904.      * returns, variables are in a state as if readPrefs() had just been
  905.      * called.  Specifically, the prefs resource has been detached.
  906.      * Subclasses may override this method, but normally shouldn't have to.
  907.      */
  908. {
  909.     short oldResFile;
  910.     Handle oldPrefsHndl;
  911.     short theReturnVal;
  912.     Boolean oldResLoad;
  913.     
  914.     checkPrefsForAcceptability();
  915.     
  916.     ASSERT(getPrefsCanBeWritten());
  917.     
  918.     oldResFile = CurResFile();
  919.     oldResLoad = ResLoad;
  920.     
  921.     TRY {
  922.         MakeCurrent();
  923.         SetResLoad(FALSE);
  924.         
  925.         oldPrefsHndl = getPrefsHndl();
  926.         
  927.         SetResLoad(TRUE);
  928.         
  929.         if (oldPrefsHndl != NULL) {
  930.             RmveResource(oldPrefsHndl);
  931.             DisposHandle(oldPrefsHndl);
  932.         }
  933.         
  934.         addPrefsHndl(valueOfPrefsHndl());
  935.         UpdateResFile(this->refNum);
  936.         
  937.         DetachResource(valueOfPrefsHndl());
  938.         forgetPrefsHndl();
  939.         
  940.         readPrefs();
  941.         
  942.         SetResLoad(oldResLoad);
  943.         UseResFile(oldResFile);
  944.     } CATCH {
  945.         SetResLoad(oldResLoad);
  946.         UseResFile(oldResFile);
  947.     } ENDTRY;
  948. }
  949.  
  950.  
  951.  
  952. void CPrefsFile::readDefaultPrefs(void)
  953.     /* Gets the default preferences from the appropriate resource in the
  954.      * application.
  955.      * Subclasses may override this method, but normally shouldn't have to.
  956.      */
  957. {
  958.     Handle defPrefsHndl;
  959.     short oldResFile;
  960.     
  961.     isReadingDefaultPrefs = TRUE;
  962.     
  963.     forgetPrefsHndl();
  964.     
  965.     oldResFile = CurResFile();
  966.     
  967.     TRY {
  968.         UseResFile(itsApplRefNum);
  969.         assignToPrefsHndl(getPrefsHndl());
  970.         if (valueOfPrefsHndl() == NULL && itsProjRsrcRefNum != 0) {
  971.             UseResFile(itsProjRsrcRefNum);
  972.             assignToPrefsHndl(getPrefsHndl());
  973.             if (valueOfPrefsHndl() == NULL) FailResError();
  974.         }
  975.         DetachResource(valueOfPrefsHndl());
  976.         
  977.         checkPrefsForAcceptability();
  978.         
  979.         UseResFile(oldResFile);
  980.         isReadingDefaultPrefs = FALSE;
  981.     } CATCH {
  982.         UseResFile(oldResFile);
  983.         isReadingDefaultPrefs = FALSE;
  984.     } ENDTRY;
  985. }
  986.